package com.mvc.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.mvc.adaptor.HandlerAdapter;
import com.mvc.annotation.Adaptor;
import com.mvc.annotation.ArgResover;
import com.mvc.annotation.Autowire;
import com.mvc.annotation.MvcController;
import com.mvc.annotation.RequestMapping;
import com.mvc.annotation.Service;
import com.mvc.annotation.Value;
import com.mvc.argumentResolver.ArgResolverInterface;
import com.mvc.viewResolver.ViewResolver;

/**
 * mvc组件之派发中心(大脑核心)
 * 
 * @author ZGame
 *
 */
public class DispatcherServlet extends HttpServlet {
	private static String HANDLERADAPTER = "handlerAdapter";
	private static String RESOLVER = "resolver";
	// 可以相互注入的bean容器map
	Map<String, Object> beans = new HashMap<String, Object>();
	// 方法路劲映射
	Map<String, Method> methodHandlerMap = new HashMap<String, Method>();
	// handlerResolver参数解析map
	Map<String, ArgResolverInterface> resolverMap = new HashMap<>();
	// prop
	Properties prop = new Properties();

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public void init(ServletConfig config) throws ServletException {
		try {
			// 包扫描
			System.out.println("开始扫描包");
			Set<Class<?>> clazzSet = PackageScanner.getClasses("com.mvc");
			for (Class<?> clazz : clazzSet) {
				System.out.println(clazz.getSimpleName());
			}
			// 实例化bean
			System.out.println("开始实例化bean");
			instance(clazzSet);
			for (Map.Entry<String, Object> entry : beans.entrySet()) {
				System.out.println(entry.getKey() + ":" + entry.getValue());
			}
			// 加载properties
			InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/mvc.properties");
			try {
				prop.load(is);
			} catch (IOException e) {
				e.printStackTrace();
			}
			// ioc注入,暂时只在controller进行注入
			ioc();
			// 访问路劲,控制器映射
			methodHandlerMapping();
			for (Map.Entry<String, Method> entry : methodHandlerMap.entrySet()) {
				System.out.println(entry.getKey() + ":" + entry.getValue());
			}

		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
				| NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		}
	}

	private void instance(Set<Class<?>> clazzSet) throws InstantiationException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		for (Class<?> clazz : clazzSet) {

			String value = "";
			if (clazz.isAnnotationPresent(MvcController.class)) {
				MvcController mvcController = clazz.getAnnotation(MvcController.class);
				value = mvcController.value();
			} else if (clazz.isAnnotationPresent(Service.class)) {
				// 实例化service
				Service service = clazz.getAnnotation(Service.class);
				value = service.value();

			} else if (clazz.isAnnotationPresent(Adaptor.class)) {
				Adaptor adaptor = clazz.getAnnotation(Adaptor.class);
				value = adaptor.value();
			} else if (clazz.isAnnotationPresent(ArgResover.class)) {
				ArgResover argResover = clazz.getAnnotation(ArgResover.class);
				value = argResover.value();
			} else {
				continue;
			}
			if (value.isEmpty()) {
				value = clazz.getSimpleName().toLowerCase();
			}

			beans.put(value, clazz.getDeclaredConstructor().newInstance());
			if (clazz.isAnnotationPresent(ArgResover.class)) {
				ArgResover argResover = clazz.getAnnotation(ArgResover.class);
				String resoveClass = argResover.value();
				Object obj = clazz.getDeclaredConstructor().newInstance();
				if (obj instanceof ArgResolverInterface) {
					resolverMap.put(resoveClass, (ArgResolverInterface) obj);
				}
			}
		}
	}

	private void ioc() throws IllegalArgumentException, IllegalAccessException {
		if (beans.size() <= 0) {
			System.out.println("没有类的实例化");
			return;
		}
		for (Map.Entry<String, Object> entry : beans.entrySet()) {
			Object bean = entry.getValue();
			Class<? extends Object> clazz = bean.getClass();
			if (clazz.isAnnotationPresent(MvcController.class)) {
				Field[] fields = clazz.getDeclaredFields();
				for (Field field : fields) {
					if (field.isAnnotationPresent(Autowire.class)) {
						Autowire autowire = field.getAnnotation(Autowire.class);
						field.setAccessible(true);
						String value = autowire.value();
						if (value.isEmpty()) {
							value = field.getName();
						}
						field.set(bean, beans.get(value.toLowerCase()));
					} else if (field.isAnnotationPresent(Value.class)) {
						Value valueAnnotation = field.getAnnotation(Value.class);
						String property = prop.getProperty(valueAnnotation.value());
						field.set(bean, property);
					}
				}
			}
		}
	}

	public void methodHandlerMapping() {
		if (beans.size() <= 0) {
			System.out.println("没有类的实例化");
			return;
		}
		for (Map.Entry<String, Object> entry : beans.entrySet()) {
			String path = entry.getKey();
			Object bean = entry.getValue();
			Class<?> clazz = bean.getClass();
			if (clazz.isAnnotationPresent(MvcController.class)) {
				Method[] methods = clazz.getMethods();
				for (Method method : methods) {
					if (method.isAnnotationPresent(RequestMapping.class)) {
						RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
						String value = requestMapping.value();
						if (value.isEmpty()) {
							value = method.getName();
							methodHandlerMap.put("/" + path + "/" + value, method);
						}
					}
				}
			}
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String uri = req.getRequestURI();
		String contextPath = req.getContextPath();
		String path = uri.replace(contextPath, "");
		Method method = methodHandlerMap.get(path);
		if (method == null) {
			return;
		}
		Object controllerBean = beans.get(method.getDeclaringClass().getSimpleName().toLowerCase());
		HandlerAdapter handlerAdapter = (HandlerAdapter) beans.get(prop.getProperty(HANDLERADAPTER));
		Object[] args = handlerAdapter.hand(req, resp, method, resolverMap);
		try {
			Object result = method.invoke(controllerBean, args);
			if (result != null && result instanceof String) {
				ViewResolver viewResolver = (ViewResolver) beans.get(prop.getProperty(RESOLVER));
				viewResolver.render((String)result, req, resp);
			}
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			e.printStackTrace();
		}
	}
}
